#ifndef __MATH_RANDOM_H__
#define __MATH_RANDOM_H__

/*
===============================================================================

Random integer number generator

===============================================================================
*/

namespace inf {

    class Random {
    public:
        Random( int seed = 0 );

        void				set_seed( int seed );
        int					get_seed( void ) const;

        int					random_int( void );			// random integer in the range [0, MAX_RAND]
        int					random_int( int maxValue );		// random integer in the range [0, maxValue[
        float				random_float( void );		// random number in the range [0.0f, 1.0f[
        float				crandom_float( void );		// random number in the range [-1.0f, 1.0f[
        Vec3				random_vector_in_clone( float halfOpeningAngle ); //Random vector in the cone with angle 2 * halfOpeningAngle centered around the z-axis 
        Vec3				random_vector_in_clone( Vec3 dir, float halfOpeningAngle );
        static const int	MAX_RAND = 0x7fff;
        static Random&		static_random( void ) { return staticRandom; }

    private:
        static Random		staticRandom;
        int					seed;
    };

    INF_INLINE Random::Random( int seed ) {
        this->seed = seed;
    }

    INF_INLINE void Random::set_seed( int seed ) {
        this->seed = seed;
    }

    INF_INLINE int Random::get_seed( void ) const {
        return seed;
    }

    INF_INLINE int Random::random_int( void ) {
        seed = 69069 * seed + 1;
        return ( seed & Random::MAX_RAND );
    }

    INF_INLINE int Random::random_int( int maxValue ) {
        if ( maxValue == 0 ) {
            return 0;			// avoid divide by zero error
        }
        return random_int() % maxValue;
    }

    INF_INLINE float Random::random_float( void ) {
        return ( random_int() / ( float )( Random::MAX_RAND + 1 ) );
    }

    INF_INLINE float Random::crandom_float( void ) {
        return ( 2.0f * ( random_float() - 0.5f ) );
    }


    /*
    ===============================================================================

    Random long number generator

    ===============================================================================
    */

    class RandomL {
    public:
        RandomL( unsigned long seed = 0 );

        void					set_seed( unsigned long seed );
        unsigned long			get_seed( void ) const;

        int						random_int( void );			// random integer in the range [0, MAX_RAND]
        int						random_int( int maxValue );	// random integer in the range [0, maxValue[
        float					random_float( void );		// random number in the range [0.0f, 1.0f[
        float					crandom_float( void );		// random number in the range [-1.0f, 1.0f[

        static const int		MAX_RAND = 0x7fff;

    private:
        unsigned long			seed;

        static const unsigned long	IEEE_ONE = 0x3f800000;
        static const unsigned long	IEEE_MASK = 0x007fffff;
    };

    INF_INLINE RandomL::RandomL( unsigned long seed ) {
        this->seed = seed;
    }

    INF_INLINE void RandomL::set_seed( unsigned long seed ) {
        this->seed = seed;
    }

    INF_INLINE unsigned long RandomL::get_seed( void ) const {
        return seed;
    }

    INF_INLINE int RandomL::random_int( void ) {
        seed = 1664525L * seed + 1013904223L;
        return ( (int) seed & RandomL::MAX_RAND );
    }

    INF_INLINE int RandomL::random_int( int maxValue ) {
        if ( maxValue == 0 ) {
            return 0;		// avoid divide by zero error
        }
        return ( random_int() >> ( 16 - math::bits_for_integer( maxValue ) ) ) % maxValue;
    }

    INF_INLINE float RandomL::random_float( void ) {
        unsigned long i;
        seed = 1664525L * seed + 1013904223L;
        i = RandomL::IEEE_ONE | ( seed & RandomL::IEEE_MASK );
        return ( ( *(float *)&i ) - 1.0f );
    }

    INF_INLINE float RandomL::crandom_float( void ) {
        unsigned long i;
        seed = 1664525L * seed + 1013904223L;
        i = RandomL::IEEE_ONE | ( seed & RandomL::IEEE_MASK );
        return ( 2.0f * ( *(float *)&i ) - 3.0f );
    }

}   // ::inf

#endif /* !__MATH_RANDOM_H__ */
